home *** CD-ROM | disk | FTP | other *** search
/ Sun Solutions 1997 April to September / Sun Solutions CD - APR '97 - SEP '97 (704-3778-12 Rev. H)(Sun Microsystems, Inc.)(1997).iso / products / Hyperion / src / cdrom.c < prev    next >
C/C++ Source or Header  |  1997-02-26  |  14KB  |  637 lines

  1. /*
  2.  * @(#)cdrom.c    1.9    4/11/94
  3.  *
  4.  * Interface between most of WorkMan and the low-level CD-ROM library
  5.  * routines defined in plat_*.c and drv_*.c.  The goal is to have no
  6.  * platform- or drive-dependent code here.
  7.  */
  8. static char *ident = "@(#)cdrom.c    1.9 4/11/94";
  9.  
  10. #include <errno.h>
  11. #include <stdio.h>
  12. #include <sys/types.h>
  13. #include <sys/time.h>
  14.  
  15. #include "struct.h"
  16.  
  17. extern struct wm_drive generic_proto, toshiba_proto, sony_proto,
  18.     toshiba33_proto;
  19.  
  20. /*
  21.  * The supported drive types are listed here.  NULL means match anything.
  22.  * The first match in the list is used, and substring matches are done (so
  23.  * put long names before their shorter prefixes.)
  24.  */
  25. struct drivelist {
  26.     char        *ven;
  27.     char        *mod;
  28.     char        *rev;
  29.     struct wm_drive    *proto;
  30. } drives[] = {
  31. {    "TOSHIBA",    "XM-3401",    NULL,    &toshiba_proto        },
  32. {    "TOSHIBA",    "XM-3301",    NULL,    &toshiba_proto        },
  33. {    "SONY",        "CDU-8012",    NULL,    &sony_proto        },
  34. {    NULL,        NULL,        NULL,    &generic_proto        }
  35. };
  36.  
  37. void *malloc();
  38. char *strchr();
  39.  
  40. extern struct play *playlist;
  41. extern struct cdinfo_wm thiscd, *cd;
  42.  
  43. /*
  44.  * Solaris 2.2 will remove the device out from under us.  Getting an ENOENT
  45.  * is therefore sometimes not a problem.
  46.  */
  47. int    intermittent_dev = 0;
  48.  
  49. /*
  50.  * Do we want to keep the CD device open after quitting by default?
  51.  */
  52. int    keep_open = 0;
  53.  
  54. char    *cd_device = NULL;
  55.  
  56. extern int cur_track, cur_index, cur_lasttrack, cur_firsttrack, cur_pos_abs,    
  57.     cur_frame, cur_pos_rel, cur_tracklen, cur_cdlen, cur_ntracks,    
  58.     cur_nsections, cur_listno, cur_stopmode, exit_on_eject,
  59.     cur_balance;
  60. extern enum cd_modes cur_cdmode;
  61. extern char *cur_artist, *cur_cdname, *cur_trackname;
  62. extern char    cur_contd, cur_avoid;
  63.  
  64. struct wm_drive    drive = { -1, NULL, NULL, NULL, NULL };
  65.  
  66. /*
  67.  * Figure out which prototype drive structure we should be using based
  68.  * on the vendor, model, and revision of the current drive.
  69.  */
  70. struct wm_drive *
  71. find_drive_struct(vendor, model, rev)
  72.     char    *vendor, *model, *rev;
  73. {
  74.     struct drivelist    *d;
  75.  
  76.     for (d = drives; d; d++)
  77.     {
  78.         if (d->ven != NULL && strncmp(d->ven, vendor, strlen(d->ven)) ||
  79.             d->mod != NULL && strncmp(d->mod, model, strlen(d->mod)) ||
  80.             d->rev != NULL && strncmp(d->rev, rev, strlen(d->rev)))
  81.             continue;
  82.         
  83.         if (d->proto->vendor[0] == '\0')
  84.             strcpy(d->proto->vendor, vendor);
  85.         if (d->proto->model[0] == '\0')
  86.             strcpy(d->proto->model, model);
  87.  
  88.         return (d->proto);
  89.     }
  90.  
  91.     return (NULL);    /* this means the list is badly terminated. */
  92. }
  93.  
  94. /*
  95.  * read_toc()
  96.  *
  97.  * Read the table of contents from the CD.  Return a pointer to a cdinfo_wm
  98.  * struct containing the relevant information (minus artist/cdname/etc.)
  99.  * This is a static struct.  Returns NULL if there was an error.
  100.  *
  101.  * XXX allocates one trackinfo too many.
  102.  */
  103. struct cdinfo_wm *
  104. read_toc()
  105. {
  106.     struct playlist        *l;
  107.     int            i, pos;
  108.  
  109.     if ((drive.get_trackcount)(&drive, &thiscd.ntracks) < 0)
  110.     {
  111.         perror("trackcount");
  112.         return (NULL);
  113.     }
  114.  
  115.     thiscd.artist[0] = thiscd.cdname[0] = '\0';
  116.     thiscd.whichdb = thiscd.otherrc = thiscd.otherdb = thiscd.user = NULL;
  117.     thiscd.length = 0;
  118.     thiscd.autoplay = thiscd.playmode = thiscd.volume = 0;
  119.  
  120.     /* Free up any left-over playlists. */
  121.     if (thiscd.lists != NULL)
  122.     {
  123.         for (l = thiscd.lists; l->name != NULL; l++)
  124.         {
  125.             free(l->name);
  126.             free(l->list);
  127.         }
  128.         free(thiscd.lists);
  129.         thiscd.lists = NULL;
  130.     }
  131.  
  132.     if (thiscd.trk != NULL)
  133.         free(thiscd.trk);
  134.  
  135.     thiscd.trk = malloc((thiscd.ntracks + 1) * sizeof(struct trackinfo));
  136.     if (thiscd.trk == NULL)
  137.     {
  138.         perror("malloc");
  139.         return (NULL);
  140.     }
  141.  
  142.     for (i = 0; i < thiscd.ntracks; i++)
  143.     {
  144.         if ((drive.get_trackinfo)(&drive, i + 1, &thiscd.trk[i].data,
  145.                     &thiscd.trk[i].start) < 0)
  146.         {
  147.             perror("CD track info read");
  148.             return (NULL);
  149.         }
  150.  
  151.         thiscd.trk[i].avoid = thiscd.trk[i].data;
  152.         thiscd.trk[i].length = thiscd.trk[i].start / 75;
  153.  
  154.         thiscd.trk[i].songname = thiscd.trk[i].otherrc =
  155.         thiscd.trk[i].otherdb = NULL;
  156.         thiscd.trk[i].contd = 0;
  157.         thiscd.trk[i].volume = 0;
  158.         thiscd.trk[i].track = i + 1;
  159.         thiscd.trk[i].section = 0;
  160.     }
  161.  
  162.     if ((drive.get_cdlen)(&drive, &thiscd.trk[i].start) < 0)
  163.     {
  164.         perror("CD length read");
  165.         return (NULL);
  166.     }
  167.     thiscd.trk[i].length = thiscd.trk[i].start / 75;
  168.  
  169. /* Now compute actual track lengths. */
  170.     pos = thiscd.trk[0].length;
  171.  
  172.     for (i = 0; i < thiscd.ntracks; i++)
  173.     {
  174.         thiscd.trk[i].length = thiscd.trk[i+1].length - pos;
  175.         pos = thiscd.trk[i+1].length;
  176.         if (thiscd.trk[i].data)
  177.             thiscd.trk[i].length = (thiscd.trk[i + 1].start -
  178.                 thiscd.trk[i].start) * 2;
  179.         if (thiscd.trk[i].avoid)
  180.             strmcpy(&thiscd.trk[i].songname, "DATA TRACK");
  181.     }
  182.  
  183.     thiscd.length = thiscd.trk[thiscd.ntracks].length;
  184.  
  185.     return (&thiscd);
  186. }
  187.  
  188. /*
  189.  * cd_status()
  190.  *
  191.  * Return values:
  192.  *
  193.  *    0    No CD in drive.
  194.  *    1    CD in drive.
  195.  *    2    CD has just been inserted (TOC has been read)
  196.  *
  197.  * Updates cur_track, cur_pos_rel, cur_pos_abs and other variables.
  198.  */
  199. int
  200. cd_status()
  201. {
  202.     static enum cd_modes    oldmode = UNKNOWN;
  203.     enum cd_modes        mode;
  204.     int            status, trackno;
  205.     int            ret = 1;
  206.  
  207.     /* Open the drive.  This returns 1 if the device isn't ready. */
  208.     status = wmcd_open(&drive);
  209.     if (status < 0)
  210.         return (status);
  211.     if (status > 0)
  212.         return (0);
  213.  
  214.     /* If the user hit the stop button, don't pass PLAYING as oldmode. */
  215.     if (cur_cdmode == STOPPED)
  216.         oldmode = STOPPED;
  217.  
  218.     if ((drive.get_drive_status)(&drive, oldmode, &mode, &cur_frame,
  219.                     &trackno, &cur_index) < 0)
  220.     {
  221.         perror("CD get drive status");
  222.         return (-1);
  223.     }
  224.     oldmode = mode;
  225.  
  226.     if (mode == EJECTED || mode == UNKNOWN)
  227.     {
  228.         cur_cdmode = EJECTED;
  229.         cur_track = -1;
  230.         cur_cdlen = cur_tracklen = 1;
  231.         cur_pos_abs = cur_pos_rel = cur_frame = 0;
  232.  
  233.         if (exit_on_eject)
  234.             exit(0);
  235.  
  236.         return (0);
  237.     }
  238.  
  239.     /* If there wasn't a CD before and there is now, learn about it. */
  240.     if (cur_cdmode == EJECTED)
  241.     {
  242.         cur_pos_rel = cur_pos_abs = 0;
  243.  
  244.         if ((cd = read_toc()) == NULL)
  245.             if (exit_on_eject)
  246.                 exit(-1);
  247.             else
  248.                 return (-1);
  249.  
  250.         cur_nsections = 0;
  251.         cur_ntracks = cd->ntracks;
  252.         cur_cdlen = cd->length;
  253.         load();
  254.         cur_artist = cd->artist;
  255.         cur_cdname = cd->cdname;
  256.         cur_cdmode = STOPPED;
  257.         ret = 2;
  258.     }
  259.  
  260.     switch (mode) {
  261.     case PLAYING:
  262.     case PAUSED:
  263.         cur_pos_abs = cur_frame / 75;
  264.  
  265.         /* Only look up the current track number if necessary. */
  266.         if (cur_track < 1 || cur_frame < cd->trk[cur_track-1].start ||
  267.                 cur_frame >= (cur_track >= cur_ntracks ?
  268.                 (cur_cdlen + 1) * 75 :
  269.                 cd->trk[cur_track].start))
  270.         {
  271.             cur_track = 0;
  272.             while (cur_track < cur_ntracks && cur_frame >=
  273.                     cd->trk[cur_track].start)
  274.                 cur_track++;
  275.         }
  276.         if (cur_track >= 1 && trackno > cd->trk[cur_track-1].track)
  277.             cur_track++;
  278.         /* Fall through */
  279.  
  280.     case UNKNOWN:
  281.         if (mode == UNKNOWN)
  282.         {
  283.             mode = STOPPED;
  284.             cur_lasttrack = cur_firsttrack = -1;
  285.         }
  286.         /* Fall through */
  287.  
  288.     case STOPPED:
  289.         if (cur_track >= 1 && cur_track <= cur_ntracks)
  290.         {
  291.             cur_trackname = cd->trk[cur_track-1].songname;
  292.             cur_avoid = cd->trk[cur_track-1].avoid;
  293.             cur_contd = cd->trk[cur_track-1].contd;
  294.             cur_pos_rel = (cur_frame -
  295.                 cd->trk[cur_track-1].start) / 75;
  296.             if (cur_pos_rel < 0)
  297.                 cur_pos_rel = -cur_pos_rel;
  298.         }
  299.  
  300.         if (playlist != NULL && playlist[0].start)
  301.         {
  302.             cur_pos_abs -= cd->trk[playlist[cur_listno-1].
  303.                 start - 1].start / 75;
  304.             cur_pos_abs += playlist[cur_listno-1].starttime;
  305.         }
  306.         if (cur_pos_abs < 0)
  307.             cur_pos_abs = cur_frame = 0;
  308.  
  309.         if (cur_track < 1)
  310.             cur_tracklen = cd->length;
  311.         else
  312.             cur_tracklen = cd->trk[cur_track-1].length;
  313.         /* Fall through */
  314.  
  315.     case TRACK_DONE:
  316.         cur_cdmode = mode;
  317.         break;
  318.     }
  319.  
  320.     return (ret);
  321. }
  322.  
  323. /*
  324.  * cd_volume(vol, bal, max)
  325.  *
  326.  * Set the volume levels.  "vol" and "bal" are the volume and balance knob
  327.  * settings, respectively.  "max" is the maximum value of the volume knob
  328.  * (the balance knob is assumed to always go from 0 to 20.)
  329.  */
  330. void
  331. cd_volume(vol, bal, max)
  332.     int    vol, bal, max;
  333. {
  334.     int    left, right;
  335.  
  336. /*
  337.  * Set "left" and "right" to volume-slider values accounting for the
  338.  * balance setting.
  339.  *
  340.  * XXX - the maximum volume setting is assumed to be in the 20-30 range.
  341.  */
  342.     if (bal < 9)
  343.         right = vol - (9 - bal) * 2;
  344.     else
  345.         right = vol;
  346.     if (bal > 11)
  347.         left = vol - (bal - 11) * 2;
  348.     else
  349.         left = vol;
  350.  
  351.     left = (left * 100 + max - 1) / max;
  352.     right = (right * 100 + max - 1) / max;
  353.     if (left > 100)
  354.         left = 100;
  355.     if (right > 100)
  356.         right = 100;
  357.  
  358.     (void) (drive.set_volume)(&drive, left, right);
  359. }
  360.  
  361. /*
  362.  * pause_cd()
  363.  *
  364.  * Pause the CD, if it's in play mode.  If it's already paused, go back to
  365.  * play mode.
  366.  */
  367. void
  368. pause_cd()
  369. {
  370.     if (cur_cdmode == EJECTED)    /* do nothing if there's no CD! */
  371.         return;
  372.  
  373.     switch (cur_cdmode) {
  374.     case PLAYING:        /* playing */
  375.         cur_cdmode = PAUSED;
  376.         (drive.pause)(&drive);
  377.         break;
  378.  
  379.     case PAUSED:        /* paused */
  380.         cur_cdmode = PLAYING;
  381.         (drive.resume)(&drive);
  382.     }
  383. }
  384.  
  385. /*
  386.  * stop_cd()
  387.  *
  388.  * Stop the CD if it's not already stopped.
  389.  */
  390. void
  391. stop_cd()
  392. {
  393.     if (cur_cdmode == EJECTED)
  394.         return;
  395.  
  396.     if (cur_cdmode != STOPPED)
  397.     {
  398.         cur_lasttrack = cur_firsttrack = -1;
  399.         cur_cdmode = STOPPED;
  400.         (drive.stop)(&drive);
  401.         cur_track = 1;
  402.     }
  403. }
  404.  
  405. /*
  406.  * play_chunk(start, end)
  407.  *
  408.  * Play the CD from one position to another (both in frames.)
  409.  */
  410. void
  411. play_chunk(start, end)
  412.     int start, end;
  413. {
  414.     if (cur_cdmode == EJECTED || cd == NULL)
  415.         return;
  416.  
  417.     end--;
  418.     if (start >= end)
  419.         start = end-1;
  420.  
  421.     (drive.play)(&drive, start, end);
  422. }
  423.  
  424. /*
  425.  * play_cd(starttrack, pos, endtrack)
  426.  *
  427.  * Start playing the CD or jump to a new position.  "pos" is in seconds,
  428.  * relative to start of track.
  429.  */
  430. void
  431. play_cd(start, pos, end)
  432. int start, pos, end;
  433. {
  434.     if (cur_cdmode == EJECTED || cd == NULL)
  435.         return;
  436.  
  437.     cur_firsttrack = start;
  438.     start--;
  439.     end--;
  440.     cur_lasttrack = end;
  441.  
  442.     play_chunk(cd->trk[start].start + pos * 75, end >= cur_ntracks ?
  443.         cur_cdlen * 75 : cd->trk[end].start - 1);
  444. }
  445.  
  446. /*
  447.  * Set the offset into the current track and play.  -1 means end of track
  448.  * (i.e., go to next track.)
  449.  */
  450. void
  451. play_from_pos(pos)
  452.     int    pos;
  453. {
  454.     if (pos == -1)
  455.         if (cd)
  456.             pos = cd->trk[cur_track - 1].length - 1;
  457.     if (cur_cdmode == PLAYING)
  458.         play_cd(cur_track, pos, playlist[cur_listno-1].end);
  459. }
  460.  
  461. /*
  462.  * Eject the current CD, if there is one, and set the mode to 5.
  463.  *
  464.  * Returns 0 on success, 1 if the CD couldn't be ejected, or 2 if the
  465.  * CD contains a mounted filesystem.
  466.  */
  467. eject_cd()
  468. {
  469.     int    status;
  470.  
  471.     if (cur_cdmode == EJECTED)    /* Already ejected! */
  472.         return (0);
  473.  
  474.     status = (drive.eject)(&drive);
  475.     if (status < 0)
  476.         if (status == -3)
  477.             return (2);
  478.         else
  479.             return (1);
  480.     
  481.     if (exit_on_eject)
  482.         exit(0);
  483.  
  484.     cur_track = -1;
  485.     cur_cdlen = cur_tracklen = 1;
  486.     cur_pos_abs = cur_pos_rel = cur_frame = 0;
  487.     cur_cdmode = EJECTED;
  488.  
  489.     return (0);
  490. }
  491.  
  492. /*
  493.  * find_trkind(track, index)
  494.  *
  495.  * Start playing at a particular track and index, optionally using a particular
  496.  * frame as a starting position.  Returns a frame number near the start of the
  497.  * index mark if successful, 0 if the track/index didn't exist.
  498.  *
  499.  * This is made significantly more tedious (though probably easier to port)
  500.  * by the fact that CDROMPLAYTRKIND doesn't work as advertised.  The routine
  501.  * does a binary search of the track, terminating when the interval gets to
  502.  * around 10 frames or when the next track is encountered, at which point
  503.  * it's a fair bet the index in question doesn't exist.
  504.  */
  505. find_trkind(track, index, start)
  506.     int    track, index, start;
  507. {
  508.     int    top = 0, bottom, current, interval, ret = 0, i;
  509.  
  510.     if (cur_cdmode == EJECTED || cd == NULL)
  511.         return;
  512.  
  513.     for (i = 0; i < cur_ntracks; i++)
  514.         if (cd->trk[i].track == track)
  515.             break;
  516.     bottom = cd->trk[i].start;
  517.  
  518.     for (; i < cur_ntracks; i++)
  519.         if (cd->trk[i].track > track)
  520.             break;
  521.  
  522.     top = i == cur_ntracks ? (cd->length - 1) * 75 : cd->trk[i].start;
  523.  
  524.     if (start > bottom && start < top)
  525.         bottom = start;
  526.  
  527.     current = (top + bottom) / 2;
  528.     interval = (top - bottom) / 4;
  529.  
  530.     do {
  531.         play_chunk(current, current + 75);
  532.  
  533.         if (cd_status() != 1)
  534.             return (0);
  535.         while (cur_frame < current)
  536.             if (cd_status() != 1 || cur_cdmode != PLAYING)
  537.                 return (0);
  538.             else
  539.                 susleep(1);
  540.  
  541.         if (cd->trk[cur_track - 1].track > track)
  542.             break;
  543.  
  544.         if (cur_index >= index)
  545.         {
  546.             ret = current;
  547.             current -= interval;
  548.         }
  549.         else
  550.             current += interval;
  551.         interval /= 2;
  552.     } while (interval > 2);
  553.  
  554.     return (ret);
  555. }
  556.  
  557. /*
  558.  * Simulate usleep() using select().
  559.  */
  560. susleep(usec)
  561.     int    usec;
  562. {
  563.     struct timeval    tv;
  564.  
  565.     timerclear(&tv);
  566.     tv.tv_sec = usec / 1000000;
  567.     tv.tv_usec = usec % 1000000;
  568.     return (select(0, NULL, NULL, NULL, &tv));
  569. }
  570.  
  571. /*
  572.  * Read the initial volume from the drive, if available.  Set cur_balance to
  573.  * the balance level (0-20, 10=centered) and return the proper setting for
  574.  * the volume knob.
  575.  *
  576.  * "max" is the maximum value of the volume knob.
  577.  */
  578. read_initial_volume(max)
  579.     int max;
  580. {
  581.     int    left, right;
  582.  
  583.     if ((drive.get_volume)(&drive, &left, &right) < 0 || left == -1)
  584.         return (max);
  585.  
  586.     left = (left * max + 99) / 100;
  587.     right = (right * max + 99) / 100;
  588.  
  589.     if (left < right)
  590.     {
  591.         cur_balance = (right - left) / 2 + 11;
  592.         if (cur_balance > 20)
  593.             cur_balance = 20;
  594.  
  595.         return (right);
  596.     }
  597.     else if (left == right)
  598.     {
  599.         cur_balance = 10;
  600.         return (left);
  601.     }
  602.     else
  603.     {
  604.         cur_balance = (right - left) / 2 + 9;
  605.         if (cur_balance < 0)
  606.             cur_balance = 0;
  607.  
  608.         return (left);
  609.     }
  610. }
  611.  
  612. /*
  613.  * Prototype wm_drive structure, with generic functions.  The generic functions
  614.  * will be replaced with drive-specific functions as appropriate once the drive
  615.  * type has been sensed.
  616.  */
  617. struct wm_drive generic_proto = {
  618.     -1,            /* fd */
  619.     "\0       ",        /* vendor */
  620.     "\0               ",    /* model */
  621.     NULL,            /* aux */
  622.     NULL,            /* daux */
  623.  
  624.     gen_init,        /* functions... */
  625.     gen_get_trackcount,
  626.     gen_get_cdlen,
  627.     gen_get_trackinfo,
  628.     gen_get_drive_status,
  629.     gen_get_volume,
  630.     gen_set_volume,
  631.     gen_pause,
  632.     gen_resume,
  633.     gen_stop,
  634.     gen_play,
  635.     gen_eject
  636. };
  637.